﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Entities;
using System.Globalization;
using System.Web.UI.DataVisualization.Charting;
using System.IO;
using System.Drawing;
using System.ComponentModel.Composition;

namespace Services
{
    [Export(typeof(IChartGeneratorService))]
    public class ChartGeneratorService : IChartGeneratorService
    {
        #region IChartGenerator

        byte[] IChartGeneratorService.DrawReportsChart(FilterOperations filter, IEnumerable<AuditedOperation> operations)
        {
            var chart = new Chart();
            chart.ChartAreas.Add(new ChartArea());
            chart.Width = 900;
            chart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:#,##0}";
            chart.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
            chart.ChartAreas[0].AxisY.MajorGrid.Enabled = false;
            chart.ChartAreas[0].AxisX.Interval = 1;

            chart.Series.Add(new Series());
            chart.Series[0].ChartType = SeriesChartType.Spline;
            chart.Series[0].XValueMember = "Period";
            chart.Series[0].YValueMembers = "OperationsCount";

            chart.Series.Add(new Series());
            chart.Series[1].ChartType = SeriesChartType.FastPoint;
            chart.Series[1].XValueMember = "Period";
            chart.Series[1].YValueMembers = "OperationsCount";

            chart.ChartAreas[0].AxisX.TitleFont = chart.ChartAreas[0].AxisY.TitleFont = new Font("Microsoft Sans Serif", 12, FontStyle.Bold);
            chart.ChartAreas[0].AxisY.Title = "Operations";

            string xLabel;
            chart.DataSource = GetChartData(filter, operations, out xLabel);
            chart.ChartAreas[0].AxisX.Title = xLabel;
            chart.DataBind();

            return ConvertToByteArray(chart);
        }

        byte[] IChartGeneratorService.DrawOverViewChart(IEnumerable<AccessedExtension> accessedExtension)
        {
            var chart = new Chart();
            chart.ChartAreas.Add(new ChartArea { Area3DStyle = new ChartArea3DStyle { Enable3D = true, Inclination = 55 } });
            chart.Legends.Add("");
            chart.Width = 450;

            chart.Series.Add(new Series { ChartType = SeriesChartType.Pie, XValueMember = "FileType", YValueMembers = "Count" });
            chart.Series[0]["PieLabelStyle"] = "outside";
            chart.Series[0]["PieStartAngle"] = "90";
            chart.Series[0].LegendText = "#VALX" + " " + "#PERCENT{P2}";

            chart.DataSource = accessedExtension;
            chart.DataBind();

            if (chart.Series[0].Points.Count > 1)
            {
                chart.Series[0].Points[1]["Exploded"] = "true";
            }

            return ConvertToByteArray(chart);
        }

        #endregion

        #region Private Methods

        private IEnumerable<OperationsChartData> GetChartData(FilterOperations filter, IEnumerable<AuditedOperation> operations, out string xAxisLabel)
        {
            if (!operations.Any())
            {
                xAxisLabel = string.Empty;
                return new OperationsChartData[] { };
            }

            int maxEntries = 10;

            DateTime minDate = filter.StartDate.HasValue ? filter.StartDate.Value : operations.Min(e => e.ReceivedTime).Value;
            DateTime maxDate = filter.EndDate.HasValue ? filter.EndDate.Value : operations.Max(e => e.ReceivedTime).Value;

            var allDates = new List<DateTime>() { };

            for (var day = minDate; day <= maxDate; day = day.AddDays(1))
            {
                allDates.Add(day);
            }

            IEnumerable<OperationsChartData> operationsChartData, emptyEntries;

            if (allDates.Count <= maxEntries)
            {
                xAxisLabel = "Days";
                operationsChartData = from o in operations
                                      group o by FormatDate(o.ReceivedTime.Value, 'D') into g
                                      select new OperationsChartData { Period = g.Key, OperationsCount = g.Count() };

                emptyEntries = from d in allDates
                               group d by FormatDate(d, 'D') into g
                               select new OperationsChartData { Period = g.Key, OperationsCount = 0 };
            }

            else
            {
                xAxisLabel = "Months";
                operationsChartData = from o in operations
                                      group o by FormatDate(o.ReceivedTime.Value, 'M') into g
                                      select new OperationsChartData { Period = g.Key, OperationsCount = g.Count() };

                emptyEntries = from d in allDates
                               group d by FormatDate(d, 'M') into g
                               select new OperationsChartData { Period = g.Key, OperationsCount = 0 };
            }

            if (emptyEntries.Count() > maxEntries)
            {
                xAxisLabel = "Years";
                operationsChartData = from o in operations
                                      group o by o.ReceivedTime.Value.Year into g
                                      select new OperationsChartData { Period = g.Key.ToString(), OperationsCount = g.Count() };

                emptyEntries = from d in allDates
                               group d by d.Year into g
                               select new OperationsChartData { Period = g.Key.ToString(), OperationsCount = 0 };
            }

            return from d in emptyEntries
                   join o in operationsChartData on d.Period equals o.Period into g
                   from emptyDate in g.DefaultIfEmpty()
                   select new OperationsChartData { Period = d.Period, OperationsCount = (emptyDate == null ? 0 : emptyDate.OperationsCount) };
        }

        private string FormatDate(DateTime date, char period)
        {
            string res = string.Empty;

            switch (period)
            {
                case 'D':
                    res = date.Year + "/" + date.ToString("MMM", CultureInfo.InvariantCulture) + "/" + date.Day;
                    break;
                case 'M':
                    res = date.Year + "/" + date.ToString("MMM", CultureInfo.InvariantCulture);
                    break;
            }

            return res;
        }

        private static byte[] ConvertToByteArray(Chart chart)
        {
            MemoryStream ms = new MemoryStream();
            chart.SaveImage(ms, ChartImageFormat.Png);

            return ms.ToArray();
        }

        #endregion
    }
}